
<template>
  <div id="container">
    <el-button id="pickfiles" class="el-button el-button--primary" :size="size">
      上传
      <i class="el-icon-upload el-icon--right"></i>
    </el-button>
    <span v-if="!miniList && enableList">
      <div v-if="count_upload.total > 0" class="filelist-panel">
        <el-scrollbar style="height: 100%;">
          <el-row class="row-list" v-for="item in files">
            <el-col :span="8">
              <div style="white-space:nowrap;text-overflow:ellipsis;overflow:hidden;">
                <a href="javascript:void(0);" @click="showFiledialog(item.name)">{{ item.name }}</a>
              </div>
            </el-col>
            <el-col :span="1">
              <i v-if="Math.round(Number(item.parse)) < 100" class="el-icon-loading"></i>
            </el-col>
            <el-col :span="12">
              <span v-if="Math.round(Number(item.progress)) >= 100">
                <el-progress :percentage="Number(item.progress.toFixed(0))" status="success"></el-progress>
              </span>
              <span v-else>
                <el-progress :percentage="Number(item.progress.toFixed(0))"></el-progress>
              </span>
            </el-col>
            <el-col :span="1">
              <span v-if="Math.round(Number(item.progress)) >= 100">
                <a
                  :href="apiUrl + '/api/v2/downloader/stream/' + encodeURI(item.name) +'?hash=' + item.hash"
                >
                  <i class="el-icon-share"></i>
                </a>
              </span>
            </el-col>
          </el-row>
        </el-scrollbar>
        <div class="close-btn">
          <a @click="toMini()" style="font-size: 24px;">
            <i class="el-icon-close"></i>
          </a>
        </div>
      </div>
    </span>
    <span v-else-if="miniList && enableList">
      <div class="mini-panel">
        <el-badge :value="count_upload.total" class="item">
          <a>文件数</a>
        </el-badge>
        <el-badge :value="count_upload.parse" class="item" type="warning">
          <a>解析中</a>
        </el-badge>
        <el-badge :value="count_upload.uploading" class="item">
          <a>上传中</a>
        </el-badge>
        <el-badge :value="count_upload.uploaded" class="item" type="primary">
          <a>已完成</a>
        </el-badge>
        <div class="close-btn">
          <a @click="toLarge()" style="font-size: 24px;">
            <i class="el-icon-arrow-up"></i>
          </a>
        </div>
      </div>
    </span>
    <span v-else>
      <div></div>
    </span>
    <el-dialog v-if="selectfile" title="文件上传详细信息" :visible.sync="dialogVisible" width="40%">
      <div style="width:420;height:220px;text-align:left;">
        <el-scrollbar style="height: 100%;">
          <el-row class="row-list">
            <el-col :span="4">文件名称：</el-col>
            <el-col :span="20">{{ selectfile.name }}</el-col>
          </el-row>
          <el-row class="row-list">
            <el-col :span="4">文件哈希：</el-col>
            <el-col :span="20">{{ selectfile.hash }}</el-col>
          </el-row>
          <el-row class="row-list">
            <el-col :span="4">解析进度：</el-col>
            <el-col :span="20">
              <span v-if="Math.round(Number(selectfile.parse)) >= 100">
                <el-progress :percentage="Number(selectfile.parse.toFixed(0))" status="success"></el-progress>
              </span>
              <span v-else>
                <el-progress :percentage="Number(selectfile.parse.toFixed(0))"></el-progress>
              </span>
            </el-col>
          </el-row>
          <el-row class="row-list">
            <el-col :span="4">上传进度：</el-col>
            <el-col :span="20">
              <span v-if="Math.round(Number(selectfile.progress)) >= 100">
                <el-progress :percentage="Number(selectfile.progress.toFixed(0))" status="success"></el-progress>
              </span>
              <span v-else>
                <el-progress :percentage="Number(selectfile.progress.toFixed(0))"></el-progress>
              </span>
            </el-col>
          </el-row>
          <el-row class="row-list">
            <el-col :span="4">下载地址：</el-col>
            <el-col :span="20">
              <el-input v-model="shareUrl" placeholder size="mini" style="width:85%;"></el-input>
            </el-col>
          </el-row>
          <el-row class="row-list" v-for="item in selectfile.blocks">
            <el-col :span="16">
              <div style="white-space:nowrap;text-overflow:ellipsis;overflow:hidden;">
                索引:{{ item.blockindex }}/{{ item.blockcount }}
                哈希:{{ item.blockhash }}
                大小:{{ item.blocksize }}
              </div>
            </el-col>
            <el-col :span="8">
              <span v-if="item.progress >= 100">
                <el-progress :percentage="Number(item.progress)" status="success"></el-progress>
              </span>
              <span v-else>
                <el-progress :percentage="Number(item.progress)"></el-progress>
              </span>
            </el-col>
          </el-row>
        </el-scrollbar>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import Vue from "vue";
import axios from "axios";
import sha1 from "js-sha1";
import plupload from "plupload";
import { getEtag } from "./qetag.js";
import blockinfo from "./blockinfo.js";
import fileinfo from "./fileinfo.js";
import _ from "underscore";

export default {
  name: "elplupload",
  props: {
    apiUrl: { type: String, required: true },
    blockSize: { type: Number, default: 4 * 1024 * 1024 },
    enableList: { type: Boolean, default: true },
    size: { type: String, default: "small" }
  },
  data: function() {
    return {
      miniList: false,
      uploader: {},
      selectfile: new fileinfo(),
      dialogVisible: false,
      files: {}
    };
  },
  created() {},
  mounted() {
    this.initUpload();
  },
  computed: {
    shareUrl: {
      get: function() {
        return (
          this.apiUrl +
          "/api/v2/downloader/stream/" +
          encodeURI(this.selectfile.name) +
          "?hash=" +
          this.selectfile.hash
        );
      },
      set: function() {}
    },
    count_upload: {
      get: function() {
        let [total, parse, uploading, uploaded] = [0, 0, 0, 0];
        if (this.files) {
          for (let key in this.files) {
            total++;
            if (this.files[key].progress >= 100) {
              uploaded++;
            }
            if (this.files[key].parse < 100) {
              parse++;
            }
            if (
              this.files[key].progress < 100 &&
              this.files[key].parse >= 100
            ) {
              uploading++;
            }
          }
        }
        return {
          total: total,
          parse: parse,
          uploading: uploading,
          uploaded: uploaded
        };
      },
      set: function() {}
    }
  },
  methods: {
    initUpload() {
      var self = this;
      this.uploader = new plupload.Uploader({
        runtimes: "html5,flash,silverlight,html4",
        browse_button: "pickfiles", // you can pass an id...
        container: document.getElementById("container"), // ... or DOM Element itself
        chunk_size: "1024kb",
        multipart: false,
        multipart_params: {},
        //unique_names:true,
        filters: {
          max_file_size: "10000mb",
          mime_types: []
        },
        init: {
          PostInit: function() {},
          QueueChanged: function() {},
          BeforeChunkUpload: function(up, file, Object, blob, Offset) {
            let options = file.getNative().options;
            //2-when chunk > 0 set url
            if (options.ctx == "") {
              up.setOption(
                "url",
                self.apiUrl + "/api/v2/uploader/mkblk/" + options.blocksize
              );
            } else {
              up.setOption(
                "url",
                self.apiUrl +
                  "/api/v2/uploader/bput/" +
                  options.ctx +
                  "/" +
                  Offset
              );
            }
            delete options.ctx;
            delete options.host;
            up.setOption("multipart_params", options);
          },
          ChunkUploaded: function(up, file, result) {
            let response = JSON.parse(result.response);
            let options = file.getNative().options;
            options.host = response["host"];
            options.ctx = response["ctx"];
          },
          FilesAdded: function(up, files) {
            for (let elem of files) {
              if (elem.getNative().options == undefined) {
                self.changeFile(elem);
                up.removeFile(elem);
              }
            }
          },
          FileUploaded: function(up, file) {
            let options = file.getNative().options;
            if (self.files[options.filename].progress < 100) {
              if (
                self.files[options.filename].progress +
                  100 / options.blockcount >
                100
              ) {
                self.files[options.filename].progress = 100;
              } else {
                self.files[options.filename].progress +=
                  100 / options.blockcount;
              }
            }
            if (
              Math.round(Number(self.files[options.filename].progress)) == 100
            ) {
              self.onUploaded(self.files[options.filename]);
            }
          },
          UploadProgress: function(up, file) {
            let options = file.getNative().options;
            self.files[options.filename].blocks[
              options.blockindex
            ].progress = Math.round(Number(file.percent));
          }
        }
      });
      this.uploader.init();
    },
    upload() {
      this.uploader.start();
    },
    onUploaded(file) {
      this.$emit("onUploaded", file);
    },
    toMini() {
      this.miniList = true;
    },
    toLarge() {
      this.miniList = false;
    },
    showFiledialog(filename) {
      let self = this;
      _.defer(function() {
        self.selectfile = self.files[filename];
        self.dialogVisible = true;
      });
    },
    changeFile(elem) {
      var self = this;
      var file = elem.getNative();
      if (file) {
        //1-加入文件显示列表
        Vue.set(
          this.files,
          file.name,
          new fileinfo(file.name, "reading...", 0, 0, 0, [])
        );
        getEtag(
          sha1,
          null,
          file,
          function(hash) {
            self.files[file.name].hash = hash;
            //发起请求self.apiUrl+"/chkfile/{fkey}/{hash}"
            let url =
              self.apiUrl +
              "/api/v2/uploader/chkfile/" +
              file.name +
              "/" +
              hash;
            self.UploadCheck(url, function(data) {
              //2-判断文件是否存在
              if (data == true) {
                setTimeout(self.uploadFile(file, hash), 0);
              } else {
                //3-文件上传成功(秒传)，文件进度完成
                self.files[file.name].progress = 100;
                self.onUploaded(self.files[file.name]);
              }
            });
          },
          self.ParseProgress
        );
      }
    },
    ParseProgress(evt) {
      let reader = evt.currentTarget;
      this.files[reader.name].parse += 100 / reader.count;
    },
    UploadCheck(url, callback) {
      axios
        .get(url, {})
        .then(res => {
          callback(res.data);
        })
        .catch(error => {
          this.$message.error("文件效验出错!");
        });
    },
    uploadFile(file, hash) {
      let blockSize = this.blockSize;
      let blockCount = Math.ceil(file.size / blockSize);
      for (let i = 0; i < blockCount; i++) {
        let blockfile = file.slice(i * blockSize, (i + 1) * blockSize);
        Vue.set(
          this.files[file.name].blocks,
          i,
          new blockinfo(i, "reading...", blockCount, 0, 0, 0)
        );
        setTimeout(this.uploadBlock(file, hash, blockCount, i, blockfile), 0);
      }
    },
    uploadBlock(file, hash, blockCount, blockIndex, blockfile) {
      var reader = new FileReader();
      reader.onload = e => {
        let buffer = e.currentTarget.result;
        let blockHash = getEtag(sha1, buffer);
        let blockLength = buffer.size || buffer.length || buffer.byteLength;
        let newfile = new File([blockfile], blockIndex + "-" + file.name, {
          lastModified: Date.now()
        });
        newfile.options = {
          hash: hash,
          filename: file.name,
          filetype: file.type,
          filesize: file.size,
          blockindex: blockIndex,
          blockcount: blockCount,
          blockhash: blockHash,
          blocksize: blockLength,
          ctx: "",
          host: ""
        };
        let _block = new blockinfo(
          blockIndex,
          blockHash,
          blockCount,
          blockLength
        );
        //加入块显示列表
        Vue.set(this.files[file.name].blocks, blockIndex, _block);
        //发起请求this.apiUrl+/chkblk/{fkey}/{hash}/{blockindex}/{blockhash}
        let url =
          this.apiUrl +
          "/api/v2/uploader/chkblk/" +
          file.name +
          "/" +
          hash +
          "/" +
          blockIndex +
          "/" +
          blockHash;

        var self = this;
        this.UploadCheck(url, function(data) {
          //2-判断块是否存在
          if (data == true) {
            self.uploader.addFile(newfile, blockIndex + "-" + file.name);
            _.once(self.upload());
          } else {
            //3-跳过块当前块上传，块进度完成
            self.files[file.name].blocks[blockIndex].progress = 100;
            self.files[file.name].progress += 100 / blockCount;
          }
        });
      };
      reader.readAsArrayBuffer(blockfile);
    }
  }
};
</script>

<style scoped>
.filelist-panel {
  width: 400px;
  height: 200px;
  position: fixed;
  bottom: 5px;
  right: 5px;
  font-size: 14px;
  display: block;
  padding: 30px 5px 15px 20px;
  overflow-x: hidden;
  border-width: 1px;
  border-style: solid;
  box-sizing: border-box;
  border-color: #dfdfdf;
  background-color: #f5f5f5;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
}

.el-scrollbar__wrap {
  overflow-x: hidden;
}

.mini-panel {
  width: 400px;
  height: 40px;
  position: fixed;
  bottom: 5px;
  right: 5px;
  font-size: 14px;
  text-align: center;
  display: block;
}

.close-btn {
  position: absolute;
  top: 2px;
  right: 5px;
  cursor: pointer;
}

a {
  font-size: 12px;
  text-decoration: none;
  line-height: 25px;
  color: #1d1c1c;
}

.row-list {
  border-bottom: #606266;
  line-height: 20px;
  border: 0;
  border-bottom: 1px dashed #ccc;
  padding: 5px 0 5px 0;
}

.item {
  padding-left: 5px;
  margin-top: 15px;
  margin-right: 30px;
}

.el-dialog__body {
  padding: 15px 20px;
  color: #606266;
  font-size: 14px;
}
</style>
